STEP 3 - Review results from DTM_inferLDATopics_LabelCorpus
This notebook: 1. Reads in labeled outputs from 06_pq_model.Rmd (“06_pq_labels.csv”) 2. Joins “06_pq_labels.csv” with “01_pq_metaclean.csv” 3. Subsets dataset based on labels and percent probabilities 4. Writes subsets to CSVs
Notes
- “SUBSETTING” keyword for searching 06_pq_label_review.Rmd
#load data
# 01_pq_metaclean.csv
pq_metaclean <- data.table::fread('Data/02_Working/01_pq_metaclean.csv')
# read in columns as characters so that doc id does not read in as numeric
# 04_pq_labels.csv
pq_labels <- data.table::fread(paste0('Data/02_Working/',rFileModelNum,'_pq_labels.csv'), colClasses = 'character')
# Head displays the first 6 rows of the data.table
#head(pq_metaclean)
#head(pq_labels)
# displays column names
print("pq_metaclean columns:")
[1] "pq_metaclean columns:"
names(pq_metaclean)
[1] "Title" "Publication title" "Publication year" "Document URL" "Full text"
[6] "Links" "Section" "Publication subject" "ISSN" "Copyright"
[11] "Abstract" "Publication info" "Last updated" "Place of publication" "Location"
[16] "Author" "Publisher" "Identifier / keyword" "Source type" "ProQuest document ID"
[21] "Country of publication" "Language of publication" "Publication date" "Subject" "Database"
[26] "Document type"
print("")
[1] ""
print("pq_labels columns:")
[1] "pq_labels columns:"
names(pq_labels)
[1] "document" "topic" "val"
nrow(pq_metaclean);nrow(pq_labels)
[1] 6239
[1] 4031
join cleaned dataset with labels
# join tables
# inner_join because pq_metaclean was subset based on topic 2 when the model was re-ran in "04_pq_model.Rmd"
# so we only want where the Proquest ID exists in both the original dataset and the labels.
pq_metajoin <- pq_labels %>%
inner_join(pq_metaclean, by = c("document" = "ProQuest document ID"))
pq_metajoin <- pq_metajoin %>%
rename(`ProQuest document ID` = document)
nrow(pq_metajoin);head(pq_metajoin)
[1] 4030
pq_empty<-pq_metajoin[pq_metajoin$topic == "" | is.na(pq_metajoin$topic),]
nrow(pq_empty)
[1] 0
# write labeled corpus to CSV
outputFileName = "pq_topics"
outputFile = paste(outputFolder,rFileNum,"_",outputFileName,".xlsx",sep="")
if (!file.exists(outputFile) | overwrite) {
write.xlsx(pq_metajoin, outputFile, row.names=FALSE)
#write.htmltable(pq_metajoin,title=outputFile, outputFile, sortby="topic","val")
}
Code now ready for SUBSETTING if you want to skip the following sections –
exploratory data analysis of topics generated
Do we want to keep both topics, or drop one of the topics and dig deeper into the other topic?
number of topics by publication title
plotTitle = "Count of Articles by Topic and Publication Title"
# count by pub title and topic
count_topic_pubtitle <-pq_metajoin %>%
group_by(`Publication title`,topic) %>%
summarise(n= n()) %>%
arrange(as.numeric(topic),as.numeric(n))
`summarise()` has grouped output by 'Publication title'. You can override using the `.groups` argument.
count_topic_pubtitle
# save plot in png format
outputPNGFileName <- file.path(outputPngFolder,paste0("count_topic_pubtitle.png"))
png(outputPNGFileName,height=6,width=12, units='in', res=300)
ggplot(count_topic_pubtitle, aes(x = as.factor(as.numeric(topic)), y = n, fill = `Publication title`, label = n )) +
geom_bar(stat = "identity") +
geom_text(size = 3, position = position_stack(vjust = 0.5)) +
ggtitle(plotTitle) +
theme(plot.title = element_text(hjust = 0.5)) +
xlab("Topic") +
ylab("Article Count") +
labs(fill = "Topic")
print(paste("Plot saved as:",outputPNGFileName))
[1] "Plot saved as: Images//07_pq_review/count_topic_pubtitle.png"
dev.off()
null device
1
knitr::include_graphics(paste0("Images/",rFileNum,"_pq_review/count_topic_pubtitle.png"))

number of topics by location
Count articles by topic and year
Important to consider the total articles/year in addition to the raw count of articles per topic. Peaks in articles may simply be due to an overall increase of articles for a particular year.
# article count per year and topic
count_topic_year <-pq_metajoin %>%
group_by(`Publication year`,topic) %>%
summarise(n= n()) %>%
arrange(as.numeric(topic),as.numeric(`Publication year`))
`summarise()` has grouped output by 'Publication year'. You can override using the `.groups` argument.
count_topic_year
# article count per year
count_year <-pq_metajoin %>%
group_by(`Publication year`) %>%
summarise(perYear= n()) %>%
arrange(as.numeric(`Publication year`))
count_topic_year<-count_topic_year %>%
left_join(count_year, by = "Publication year")
count_topic_year<-count_topic_year %>%
mutate(ratio = round(n/perYear,2))
plotTitle = "Count of Articles by Topic and Year"
# save plot in png format
outputPNGFileName <- file.path(outputPngFolder,paste0("count_topic_year.png"))
png(outputPNGFileName,height=5,width=15, units='in', res=300)
ggplot(count_topic_year, aes(x = `Publication year`, colour = as.factor(as.numeric(topic)))) +
geom_line(aes(y = n)) +
scale_x_continuous(breaks=seq(min(na.omit(count_topic_year$`Publication year`)), max(na.omit(count_topic_year$`Publication year`)),1)) +
theme(axis.text.x = element_text(angle = 90),
plot.title = element_text(hjust = 0.5)) +
facet_wrap(vars(as.numeric(topic))) +
ggtitle(plotTitle) +
xlab("Publication Year") +
ylab("Article Count") +
guides(colour=FALSE)
print(paste("Plot saved as:",outputPNGFileName))
[1] "Plot saved as: Images//07_pq_review/count_topic_year.png"
dev.off()
null device
1
plotTitle = "Count of Articles by Topic and Year with Year Total"
# save plot in png format
outputPNGFileName <- file.path(outputPngFolder,paste0("count_topic_year_tot.png"))
png(outputPNGFileName,height=5,width=15, units='in', res=300)
ggplot(count_topic_year, aes(x = `Publication year`, colour = as.factor(as.numeric(topic)))) +
geom_line(aes(y = n)) +
geom_line(linetype = "dashed", color="black", aes(y = perYear)) +
scale_x_continuous(breaks=seq(min(na.omit(count_topic_year$`Publication year`)), max(na.omit(count_topic_year$`Publication year`)),1)) +
theme(axis.text.x = element_text(angle = 90),
plot.title = element_text(hjust = 0.5)) +
facet_wrap(vars(as.numeric(topic))) +
ggtitle(plotTitle) +
xlab("Publication Year") +
ylab("Article Count") +
guides(colour=FALSE)
print(paste("Plot saved as:",outputPNGFileName))
[1] "Plot saved as: Images//07_pq_review/count_topic_year_tot.png"
dev.off()
null device
1
plotTitle = "Ratio of Articles by Topic and Year"
# save plot in png format
outputPNGFileName <- file.path(outputPngFolder,paste0("ratio_topic_year.png"))
png(outputPNGFileName,height=5,width=15, units='in', res=300)
ggplot(count_topic_year, aes(x = `Publication year`, colour = as.factor(as.numeric(topic)))) +
geom_line(aes(y = ratio)) +
scale_x_continuous(breaks=seq(min(na.omit(count_topic_year$`Publication year`)), max(na.omit(count_topic_year$`Publication year`)),1)) +
theme(axis.text.x = element_text(angle = 90),
plot.title = element_text(hjust = 0.5)) +
facet_wrap(vars(as.numeric(topic))) +
ggtitle(plotTitle) +
xlab("Publication Year") +
ylab("(Article Count)/(Article Total)") +
guides(colour=FALSE)
print(paste("Plot saved as:",outputPNGFileName))
[1] "Plot saved as: Images//07_pq_review/ratio_topic_year.png"
dev.off()
null device
1
knitr::include_graphics(paste0("Images/",rFileNum,"_pq_review/count_topic_year.png"))

knitr::include_graphics(paste0("Images/",rFileNum,"_pq_review/count_topic_year_tot.png"))

knitr::include_graphics(paste0("Images/",rFileNum,"_pq_review/ratio_topic_year.png"))

Coherence score by topic
knitr::include_graphics(paste0("Images/",rFileModelNum,"_pq_model/coherence_score_topic.png"))

Wordclouds
knitr::include_graphics(paste0("Images/",rFileModelNum,"_pq_model/1_lda_topic_wc.png"))

knitr::include_graphics(paste0("Images/",rFileModelNum,"_pq_model/2_lda_topic_wc.png"))

Count of Article by Topic
# bar chart
knitr::include_graphics(paste0("Images/",rFileModelNum,"_pq_model/topic_count_bar.png"))

# pie chart
knitr::include_graphics(paste0("Images/",rFileModelNum,"_pq_model/topic_count_pie.png"))

Count of Probabilities by Percentage
knitr::include_graphics(paste0("Images/",rFileModelNum,"_pq_model/prob_count.png"))

Count of Topic Probabilities by Percentage
knitr::include_graphics(paste0("Images/",rFileModelNum,"_pq_model/prob_topic_count.png"))

#5. Visualising of topics in a dendrogram - not enough topics (<2)
# knitr::include_graphics(paste0("Images/",rFileModelNum,"_pq_model/hclust_dendrogram.png"))
START SUBSETTING - Subset Function
Subset topic 1
# subset corpus to unique identifier & full text of article
# investigate topic 1
outputFileName_1t <- "pq_topic1"
minPerc <- 0
maxPerc <- 1
theTopic <- "1"
## e.g, minPerc = 0 & maxPerc = 0.4 is zero to 0.3999999999 ...
pq_topic1_subset<-topic_subset_csv(outputFolder, rFileNum, outputFileName_1t, pq_metajoin , minPerc, maxPerc, theTopic, overwrite)
Already exists, loading: pq_topic1
head(pq_topic1_subset);nrow(pq_topic1_subset)
[1] 1025
Subset topic 2
# subset corpus to unique identifier & full text of article
# investigate topic 2
outputFileName_1t <- "pq_topic2"
minPerc <- 0
maxPerc <- 1
theTopic <- "2"
## e.g, minPerc = 0 & maxPerc = 0.4 is zero to 0.3999999999 ...
pq_topic1_subset<-topic_subset_csv(outputFolder, rFileNum, outputFileName_1t, pq_metajoin , minPerc, maxPerc, theTopic, overwrite)
Already exists, loading: pq_topic2
head(pq_topic1_subset);nrow(pq_topic1_subset)
[1] 3005
Subset topic 1, 90%
# subset corpus to unique identifier & full text of article
# investigate topic 1, 90%
outputFile_1t90perc <- "pq_topic1_90perc"
minPerc <- 0.9
maxPerc <- 1
theTopic <- "1"
## e.g, minPerc = 0 & maxPerc = 0.4 is zero to 0.3999999999 ...
pq_topic1_90perc<-topic_subset_csv(outputFolder, rFileNum, outputFile_1t90perc, pq_metajoin , minPerc, maxPerc, theTopic, overwrite)
Already exists, loading: pq_topic1_90perc
head(pq_topic1_90perc);nrow(pq_topic1_90perc)
[1] 733
Subset topic 2, 90%
# subset corpus to unique identifier & full text of article
# investigate topic 2, 90%
outputFile_5t90perc <- "pq_topic2_90perc"
minPerc <- 0.9
maxPerc <- 1
theTopic <- "2"
## e.g, minPerc = 0 & maxPerc = 0.4 is zero to 0.3999999999 ...
pq_topic5_90perc<-topic_subset_csv(outputFolder, rFileNum, outputFile_5t90perc, pq_metajoin , minPerc, maxPerc, theTopic, overwrite)
Already exists, loading: pq_topic2_90perc
head(pq_topic5_90perc);nrow(pq_topic5_90perc)
[1] 1994
Subset 50-60%-ers
# subset corpus to unique identifier & full text of article
# investigate all topics, 50-60%
outputFile_56perc <- "pq_56perc"
minPerc <- 0.5
maxPerc <- 0.6
theTopic <- "all"
## e.g, minPerc = 0 & maxPerc = 0.4 is zero to 0.3999999999 ...
pq_56perc<-topic_subset_csv(outputFolder, rFileNum, outputFile_56perc, pq_metajoin , minPerc, maxPerc, theTopic, overwrite)
Already exists, loading: pq_56perc
head(pq_56perc);nrow(pq_56perc)
[1] 224
Subset 0-60%-ers
# subset corpus to unique identifier & full text of article
# investigate all topics, 0-60%
# if within the function you write overwrite=TRUE, then the output CSV file will be re-written.
# overwrite, outputFolder, rFileName are set in the first chunk of code
outputFileName_06perc <- "pq_06perc"
minPerc <- 0
maxPerc <- 0.6
theTopic <- "all"
## e.g, minPerc = 0 & maxPerc = 0.6 is zero to 0.59999999 ...
pq_perc06_subset<-topic_subset_csv(outputFolder, rFileNum, outputFileName_06perc, pq_metajoin , minPerc, maxPerc, theTopic, overwrite)
Already exists, loading: pq_06perc
head(pq_perc06_subset);nrow(pq_topic1_subset)
[1] 3005
unique(pq_perc06_subset$topic)
[1] "2" "1"
min(pq_perc06_subset$val)
[1] "0.50066"
max(pq_perc06_subset$val)
[1] "0.59996"
END SUBSETTING
explore term frequencies by year - how do words in our corpus change over time?
Referenced walk-through from: https://cran.r-project.org/web/packages/tidytext/vignettes/tidying_casting.html Are articles normalized based on the number of articles per year
# subset corpus to unique identifier & year
pq_time <- pq_metaclean %>%
select(`ProQuest document ID`, `Publication year`)
# tokens generated from 06_pq_model.Rmd
outputTokenFile = paste0(rFileModelNum,"_tokens.RData")
tokensFileName = file.path(outputFolder,outputTokenFile)
load(file=tokensFileName)
tokens <- tokens %>%
full_join(pq_time, by = c("ProQuest document ID" = "ProQuest document ID")) %>%
rename(Year = `Publication year`) %>%
rename(pq_id = `ProQuest document ID`) %>%
mutate_at(vars(Year), funs(as.integer))
`funs()` was deprecated in dplyr 0.8.0.
Please use a list of either functions or lambdas:
# Simple named list:
list(mean = mean, median = median)
# Auto named with `tibble::lst()`:
tibble::lst(mean, median)
# Using lambdas
list(~ mean(., trim = .2), ~ median(., na.rm = TRUE))
rm(pq_time)
outputFreqsFile = paste0(rFileNum,"_tokens_freq")
tokens_freq<-create_ifnot_tokens_freq(outputFolder, outputFreqsFile, tokens, overwrite)
Already exists, loading: 07_tokens_freq
outputFreqModelFile = paste0(rFileNum,"_freq_models")
freq_models <- create_ifnot_freqmodels(outputFolder, outputFreqModelFile, tokens_freq, overwrite)
Already exists, loading: 07_freq_models
model results
freq_models %>%
filter(term == "Year") %>%
arrange(desc(abs(estimate)))
Models displayed as a volcano plot, which compares the effect size with the significance
# save plot in png format
outputPNGFileName <- file.path(outputPngFolder,paste0("word_change_over_time.png"))
png(outputPNGFileName,height=5,width=15, units='in', res=300)
freq_models %>%
mutate(adjusted.p.value = p.adjust(p.value)) %>%
ggplot(aes(estimate, adjusted.p.value)) +
geom_point() +
scale_y_log10() +
geom_text(aes(label = word), vjust = 1, hjust = 1,
check_overlap = TRUE) +
xlab("Estimated change over time") +
ylab("Adjusted p-value")
print(paste("Plot saved as:",outputPNGFileName))
[1] "Plot saved as: Images//07_pq_review/word_change_over_time.png"
dev.off()
null device
1
knitr::include_graphics(paste0("Images/",rFileNum,"_pq_review/word_change_over_time.png"))

Top 6 terms that have changed in frequency over time
# save plot in png format
outputPNGFileName <- file.path(outputPngFolder,paste0("top_word_change_over_time.png"))
png(outputPNGFileName,height=5,width=15, units='in', res=300)
freq_models %>%
top_n(6, abs(estimate)) %>%
inner_join(tokens_freq) %>%
ggplot(aes(Year, percent)) +
geom_point() +
geom_smooth() +
facet_wrap(~ word) +
scale_y_continuous(labels = percent_format()) +
ylab("Frequency of word in speech")
Joining, by = "word"
print(paste("Plot saved as:",outputPNGFileName))
[1] "Plot saved as: Images//07_pq_review/top_word_change_over_time.png"
dev.off()
null device
1
knitr::include_graphics(paste0("Images/",rFileNum,"_pq_review/top_word_change_over_time.png"))

LS0tDQp0aXRsZTogInBxX2xhYmVsX3JldmlldyINCm91dHB1dDoNCiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCi0tLQ0KDQojIFNURVAgMyAtIFJldmlldyByZXN1bHRzIGZyb20gRFRNX2luZmVyTERBVG9waWNzX0xhYmVsQ29ycHVzDQoNClRoaXMgbm90ZWJvb2s6DQoxLiBSZWFkcyBpbiBsYWJlbGVkIG91dHB1dHMgZnJvbSAwNl9wcV9tb2RlbC5SbWQgKCIwNl9wcV9sYWJlbHMuY3N2IikNCjIuIEpvaW5zICIwNl9wcV9sYWJlbHMuY3N2IiB3aXRoICIwMV9wcV9tZXRhY2xlYW4uY3N2Ig0KMy4gU3Vic2V0cyBkYXRhc2V0IGJhc2VkIG9uIGxhYmVscyBhbmQgcGVyY2VudCBwcm9iYWJpbGl0aWVzDQo0LiBXcml0ZXMgc3Vic2V0cyB0byBDU1ZzDQoNCiMgTm90ZXMNCjEuICJTVUJTRVRUSU5HIiBrZXl3b3JkIGZvciBzZWFyY2hpbmcgMDZfcHFfbGFiZWxfcmV2aWV3LlJtZA0KDQpgYGB7ciwgZWNobz1GQUxTRSwgcmVzdWx0cz0iaGlkZSIsIG1lc3NhZ2VzPUZBTFNFfQ0KIyBMb2FkIGFuZCBJbnN0YWxsIExpYnJhcmllcw0Kc291cmNlKCJTRU5fZnVuY3Rpb25zLlIiKQ0KDQojIyBDaGVjayBsaWJyYXJpZXMgJiBpbnN0YWxsDQpMaWJyYXJ5TGlzdDwtYygic3RyaW5nciIsImRhdGEudGFibGUiLCJkcGx5ciIsInRpZHlyIiwibWFncml0dHIiLCJOTFAiLCJ0aWR5dGV4dCIsInRtIiwiZ2dwbG90MiIsDQogICAgICAgICAgICAgICAic2NhbGVzIiwgImdnd29yZGNsb3VkIiwidGV4dG1pbmVSIiwiZGlnZXN0IiwgImJyb29tIiwgInN0cmluZ2kiLCAieGxzeCIpDQppbnN0YWxsX29yX2xvYWRfcGFjayhMaWJyYXJ5TGlzdCkNCg0KDQpvdXRwdXRGb2xkZXIgPSAiRGF0YS8wMl9Xb3JraW5nLyINCm91dHB1dEltZ0ZvbGRlciA9ICJJbWFnZXMvIg0KckZpbGVOdW0gPSAiMDciDQpyRmlsZU1vZGVsTnVtID0gIjA2Ig0KDQpvdXRwdXRQbmdGb2xkZXI8LWZpbGUucGF0aChvdXRwdXRJbWdGb2xkZXIsIHBhc3RlMChyRmlsZU51bSwiX3BxX3JldmlldyIpKQ0KaWYgKCFkaXIuZXhpc3RzKG91dHB1dFBuZ0ZvbGRlcikpIGRpci5jcmVhdGUob3V0cHV0UG5nRm9sZGVyKQ0KDQpvdmVyd3JpdGUgPSBGQUxTRQ0KYGBgDQoNCg0KYGBge3J9DQojbG9hZCBkYXRhDQojIDAxX3BxX21ldGFjbGVhbi5jc3YNCnBxX21ldGFjbGVhbiA8LSBkYXRhLnRhYmxlOjpmcmVhZCgnRGF0YS8wMl9Xb3JraW5nLzAxX3BxX21ldGFjbGVhbi5jc3YnKQ0KIyByZWFkIGluIGNvbHVtbnMgYXMgY2hhcmFjdGVycyBzbyB0aGF0IGRvYyBpZCBkb2VzIG5vdCByZWFkIGluIGFzIG51bWVyaWMNCiMgMDRfcHFfbGFiZWxzLmNzdg0KcHFfbGFiZWxzIDwtIGRhdGEudGFibGU6OmZyZWFkKHBhc3RlMCgnRGF0YS8wMl9Xb3JraW5nLycsckZpbGVNb2RlbE51bSwnX3BxX2xhYmVscy5jc3YnKSwgY29sQ2xhc3NlcyA9ICdjaGFyYWN0ZXInKQ0KDQojIEhlYWQgZGlzcGxheXMgdGhlIGZpcnN0IDYgcm93cyBvZiB0aGUgZGF0YS50YWJsZQ0KI2hlYWQocHFfbWV0YWNsZWFuKQ0KI2hlYWQocHFfbGFiZWxzKQ0KYGBgDQoNCg0KYGBge3J9DQojIGRpc3BsYXlzIGNvbHVtbiBuYW1lcw0KcHJpbnQoInBxX21ldGFjbGVhbiBjb2x1bW5zOiIpDQpuYW1lcyhwcV9tZXRhY2xlYW4pDQpwcmludCgiIikNCnByaW50KCJwcV9sYWJlbHMgY29sdW1uczoiKQ0KbmFtZXMocHFfbGFiZWxzKQ0KbnJvdyhwcV9tZXRhY2xlYW4pO25yb3cocHFfbGFiZWxzKQ0KYGBgDQoNCiMgam9pbiBjbGVhbmVkIGRhdGFzZXQgd2l0aCBsYWJlbHMNCmBgYHtyfQ0KIyBqb2luIHRhYmxlcw0KIyBpbm5lcl9qb2luIGJlY2F1c2UgcHFfbWV0YWNsZWFuIHdhcyBzdWJzZXQgYmFzZWQgb24gdG9waWMgMiB3aGVuIHRoZSBtb2RlbCB3YXMgcmUtcmFuIGluICIwNF9wcV9tb2RlbC5SbWQiDQojIHNvIHdlIG9ubHkgd2FudCB3aGVyZSB0aGUgUHJvcXVlc3QgSUQgZXhpc3RzIGluIGJvdGggdGhlIG9yaWdpbmFsIGRhdGFzZXQgYW5kIHRoZSBsYWJlbHMuDQpwcV9tZXRham9pbiA8LSBwcV9sYWJlbHMgJT4lIA0KICBpbm5lcl9qb2luKHBxX21ldGFjbGVhbiwgYnkgPSBjKCJkb2N1bWVudCIgPSAiUHJvUXVlc3QgZG9jdW1lbnQgSUQiKSkNCg0KcHFfbWV0YWpvaW4gPC0gcHFfbWV0YWpvaW4gJT4lDQogIHJlbmFtZShgUHJvUXVlc3QgZG9jdW1lbnQgSURgID0gZG9jdW1lbnQpDQoNCm5yb3cocHFfbWV0YWpvaW4pO2hlYWQocHFfbWV0YWpvaW4pDQoNCnBxX2VtcHR5PC1wcV9tZXRham9pbltwcV9tZXRham9pbiR0b3BpYyA9PSAiIiB8IGlzLm5hKHBxX21ldGFqb2luJHRvcGljKSxdDQpucm93KHBxX2VtcHR5KQ0KDQojIHdyaXRlIGxhYmVsZWQgY29ycHVzIHRvIENTVg0Kb3V0cHV0RmlsZU5hbWUgPSAicHFfdG9waWNzIg0Kb3V0cHV0RmlsZSA9IHBhc3RlKG91dHB1dEZvbGRlcixyRmlsZU51bSwiXyIsb3V0cHV0RmlsZU5hbWUsIi54bHN4IixzZXA9IiIpDQppZiAoIWZpbGUuZXhpc3RzKG91dHB1dEZpbGUpIHwgb3ZlcndyaXRlKSB7DQogIHdyaXRlLnhsc3gocHFfbWV0YWpvaW4sIG91dHB1dEZpbGUsIHJvdy5uYW1lcz1GQUxTRSkNCiAgI3dyaXRlLmh0bWx0YWJsZShwcV9tZXRham9pbix0aXRsZT1vdXRwdXRGaWxlLCBvdXRwdXRGaWxlLCBzb3J0Ynk9InRvcGljIiwidmFsIikNCn0NCmBgYA0KIyBDb2RlIG5vdyByZWFkeSBmb3IgU1VCU0VUVElORyBpZiB5b3Ugd2FudCB0byBza2lwIHRoZSBmb2xsb3dpbmcgc2VjdGlvbnMgLS0NCg0KDQojIGV4cGxvcmF0b3J5IGRhdGEgYW5hbHlzaXMgb2YgdG9waWNzIGdlbmVyYXRlZA0KRG8gd2Ugd2FudCB0byBrZWVwIGJvdGggdG9waWNzLCBvciBkcm9wIG9uZSBvZiB0aGUgdG9waWNzIGFuZCBkaWcgZGVlcGVyIGludG8gdGhlIG90aGVyIHRvcGljPw0KDQojIG51bWJlciBvZiB0b3BpY3MgYnkgcHVibGljYXRpb24gdGl0bGUNCmBgYHtyLCBtZXNzYWdlcz1GQUxTRX0NCnBsb3RUaXRsZSA9ICJDb3VudCBvZiBBcnRpY2xlcyBieSBUb3BpYyBhbmQgUHVibGljYXRpb24gVGl0bGUiDQoNCiMgY291bnQgYnkgcHViIHRpdGxlIGFuZCB0b3BpYw0KY291bnRfdG9waWNfcHVidGl0bGUgPC1wcV9tZXRham9pbiAlPiUNCiAgZ3JvdXBfYnkoYFB1YmxpY2F0aW9uIHRpdGxlYCx0b3BpYykgJT4lIA0KICBzdW1tYXJpc2Uobj0gbigpKSAlPiUNCiAgYXJyYW5nZShhcy5udW1lcmljKHRvcGljKSxhcy5udW1lcmljKG4pKQ0KDQpjb3VudF90b3BpY19wdWJ0aXRsZQ0KDQojIHNhdmUgcGxvdCBpbiBwbmcgZm9ybWF0DQpvdXRwdXRQTkdGaWxlTmFtZSA8LSBmaWxlLnBhdGgob3V0cHV0UG5nRm9sZGVyLHBhc3RlMCgiY291bnRfdG9waWNfcHVidGl0bGUucG5nIikpDQpwbmcob3V0cHV0UE5HRmlsZU5hbWUsaGVpZ2h0PTYsd2lkdGg9MTIsIHVuaXRzPSdpbicsIHJlcz0zMDApDQpnZ3Bsb3QoY291bnRfdG9waWNfcHVidGl0bGUsIGFlcyh4ID0gYXMuZmFjdG9yKGFzLm51bWVyaWModG9waWMpKSwgeSA9IG4sIGZpbGwgPSBgUHVibGljYXRpb24gdGl0bGVgLCBsYWJlbCA9IG4gKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKw0KICBnZW9tX3RleHQoc2l6ZSA9IDMsIHBvc2l0aW9uID0gcG9zaXRpb25fc3RhY2sodmp1c3QgPSAwLjUpKSArDQogIGdndGl0bGUocGxvdFRpdGxlKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArDQogIHhsYWIoIlRvcGljIikgKw0KICB5bGFiKCJBcnRpY2xlIENvdW50IikgKw0KICBsYWJzKGZpbGwgPSAiVG9waWMiKQ0KcHJpbnQocGFzdGUoIlBsb3Qgc2F2ZWQgYXM6IixvdXRwdXRQTkdGaWxlTmFtZSkpDQpkZXYub2ZmKCkNCg0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MocGFzdGUwKCJJbWFnZXMvIixyRmlsZU51bSwiX3BxX3Jldmlldy9jb3VudF90b3BpY19wdWJ0aXRsZS5wbmciKSkNCmBgYA0KDQojIG51bWJlciBvZiB0b3BpY3MgYnkgbG9jYXRpb24NCg0KIyBDb3VudCBhcnRpY2xlcyBieSB0b3BpYyBhbmQgeWVhcg0KSW1wb3J0YW50IHRvIGNvbnNpZGVyIHRoZSB0b3RhbCBhcnRpY2xlcy95ZWFyIGluIGFkZGl0aW9uIHRvIHRoZSByYXcgY291bnQgb2YgYXJ0aWNsZXMgcGVyIHRvcGljLg0KUGVha3MgaW4gYXJ0aWNsZXMgbWF5IHNpbXBseSBiZSBkdWUgdG8gYW4gb3ZlcmFsbCBpbmNyZWFzZSBvZiBhcnRpY2xlcyBmb3IgYSBwYXJ0aWN1bGFyIHllYXIuDQpgYGB7ciwgbWVzc2FnZXM9RkFMU0V9DQojIGFydGljbGUgY291bnQgcGVyIHllYXIgYW5kIHRvcGljDQpjb3VudF90b3BpY195ZWFyIDwtcHFfbWV0YWpvaW4gJT4lDQogIGdyb3VwX2J5KGBQdWJsaWNhdGlvbiB5ZWFyYCx0b3BpYykgJT4lIA0KICBzdW1tYXJpc2Uobj0gbigpKSAlPiUNCiAgYXJyYW5nZShhcy5udW1lcmljKHRvcGljKSxhcy5udW1lcmljKGBQdWJsaWNhdGlvbiB5ZWFyYCkpDQoNCmNvdW50X3RvcGljX3llYXINCg0KIyBhcnRpY2xlIGNvdW50IHBlciB5ZWFyDQpjb3VudF95ZWFyIDwtcHFfbWV0YWpvaW4gJT4lDQogIGdyb3VwX2J5KGBQdWJsaWNhdGlvbiB5ZWFyYCkgJT4lIA0KICBzdW1tYXJpc2UocGVyWWVhcj0gbigpKSAlPiUNCiAgYXJyYW5nZShhcy5udW1lcmljKGBQdWJsaWNhdGlvbiB5ZWFyYCkpDQoNCmNvdW50X3RvcGljX3llYXI8LWNvdW50X3RvcGljX3llYXIgJT4lDQogIGxlZnRfam9pbihjb3VudF95ZWFyLCBieSA9ICJQdWJsaWNhdGlvbiB5ZWFyIikNCg0KY291bnRfdG9waWNfeWVhcjwtY291bnRfdG9waWNfeWVhciAlPiUNCiAgbXV0YXRlKHJhdGlvID0gcm91bmQobi9wZXJZZWFyLDIpKQ0KDQpwbG90VGl0bGUgPSAiQ291bnQgb2YgQXJ0aWNsZXMgYnkgVG9waWMgYW5kIFllYXIiDQojIHNhdmUgcGxvdCBpbiBwbmcgZm9ybWF0DQpvdXRwdXRQTkdGaWxlTmFtZSA8LSBmaWxlLnBhdGgob3V0cHV0UG5nRm9sZGVyLHBhc3RlMCgiY291bnRfdG9waWNfeWVhci5wbmciKSkNCnBuZyhvdXRwdXRQTkdGaWxlTmFtZSxoZWlnaHQ9NSx3aWR0aD0xNSwgdW5pdHM9J2luJywgcmVzPTMwMCkNCmdncGxvdChjb3VudF90b3BpY195ZWFyLCBhZXMoeCA9IGBQdWJsaWNhdGlvbiB5ZWFyYCwgY29sb3VyID0gYXMuZmFjdG9yKGFzLm51bWVyaWModG9waWMpKSkpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gbikpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1zZXEobWluKG5hLm9taXQoY291bnRfdG9waWNfeWVhciRgUHVibGljYXRpb24geWVhcmApKSwgbWF4KG5hLm9taXQoY291bnRfdG9waWNfeWVhciRgUHVibGljYXRpb24geWVhcmApKSwxKSkgKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSwNCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsNCiAgZmFjZXRfd3JhcCh2YXJzKGFzLm51bWVyaWModG9waWMpKSkgKw0KICBnZ3RpdGxlKHBsb3RUaXRsZSkgKw0KICB4bGFiKCJQdWJsaWNhdGlvbiBZZWFyIikgKw0KICB5bGFiKCJBcnRpY2xlIENvdW50IikgKw0KICBndWlkZXMoY29sb3VyPUZBTFNFKSANCnByaW50KHBhc3RlKCJQbG90IHNhdmVkIGFzOiIsb3V0cHV0UE5HRmlsZU5hbWUpKQ0KZGV2Lm9mZigpDQoNCnBsb3RUaXRsZSA9ICJDb3VudCBvZiBBcnRpY2xlcyBieSBUb3BpYyBhbmQgWWVhciB3aXRoIFllYXIgVG90YWwiDQojIHNhdmUgcGxvdCBpbiBwbmcgZm9ybWF0DQpvdXRwdXRQTkdGaWxlTmFtZSA8LSBmaWxlLnBhdGgob3V0cHV0UG5nRm9sZGVyLHBhc3RlMCgiY291bnRfdG9waWNfeWVhcl90b3QucG5nIikpDQpwbmcob3V0cHV0UE5HRmlsZU5hbWUsaGVpZ2h0PTUsd2lkdGg9MTUsIHVuaXRzPSdpbicsIHJlcz0zMDApDQpnZ3Bsb3QoY291bnRfdG9waWNfeWVhciwgYWVzKHggPSBgUHVibGljYXRpb24geWVhcmAsIGNvbG91ciA9IGFzLmZhY3Rvcihhcy5udW1lcmljKHRvcGljKSkpKSArDQogIGdlb21fbGluZShhZXMoeSA9IG4pKSArDQogIGdlb21fbGluZShsaW5ldHlwZSA9ICJkYXNoZWQiLCBjb2xvcj0iYmxhY2siLCBhZXMoeSA9IHBlclllYXIpKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKG1pbihuYS5vbWl0KGNvdW50X3RvcGljX3llYXIkYFB1YmxpY2F0aW9uIHllYXJgKSksIG1heChuYS5vbWl0KGNvdW50X3RvcGljX3llYXIkYFB1YmxpY2F0aW9uIHllYXJgKSksMSkpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCksDQogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArDQogIGZhY2V0X3dyYXAodmFycyhhcy5udW1lcmljKHRvcGljKSkpICsNCiAgZ2d0aXRsZShwbG90VGl0bGUpICsNCiAgeGxhYigiUHVibGljYXRpb24gWWVhciIpICsNCiAgeWxhYigiQXJ0aWNsZSBDb3VudCIpICsNCiAgZ3VpZGVzKGNvbG91cj1GQUxTRSkgDQpwcmludChwYXN0ZSgiUGxvdCBzYXZlZCBhczoiLG91dHB1dFBOR0ZpbGVOYW1lKSkNCmRldi5vZmYoKQ0KDQpwbG90VGl0bGUgPSAiUmF0aW8gb2YgQXJ0aWNsZXMgYnkgVG9waWMgYW5kIFllYXIiDQojIHNhdmUgcGxvdCBpbiBwbmcgZm9ybWF0DQpvdXRwdXRQTkdGaWxlTmFtZSA8LSBmaWxlLnBhdGgob3V0cHV0UG5nRm9sZGVyLHBhc3RlMCgicmF0aW9fdG9waWNfeWVhci5wbmciKSkNCnBuZyhvdXRwdXRQTkdGaWxlTmFtZSxoZWlnaHQ9NSx3aWR0aD0xNSwgdW5pdHM9J2luJywgcmVzPTMwMCkNCmdncGxvdChjb3VudF90b3BpY195ZWFyLCBhZXMoeCA9IGBQdWJsaWNhdGlvbiB5ZWFyYCwgY29sb3VyID0gYXMuZmFjdG9yKGFzLm51bWVyaWModG9waWMpKSkpICsNCiAgZ2VvbV9saW5lKGFlcyh5ID0gcmF0aW8pKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKG1pbihuYS5vbWl0KGNvdW50X3RvcGljX3llYXIkYFB1YmxpY2F0aW9uIHllYXJgKSksIG1heChuYS5vbWl0KGNvdW50X3RvcGljX3llYXIkYFB1YmxpY2F0aW9uIHllYXJgKSksMSkpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCksDQogICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUpKSArDQogIGZhY2V0X3dyYXAodmFycyhhcy5udW1lcmljKHRvcGljKSkpICsNCiAgZ2d0aXRsZShwbG90VGl0bGUpICsNCiAgeGxhYigiUHVibGljYXRpb24gWWVhciIpICsNCiAgeWxhYigiKEFydGljbGUgQ291bnQpLyhBcnRpY2xlIFRvdGFsKSIpICsNCiAgZ3VpZGVzKGNvbG91cj1GQUxTRSkgDQpwcmludChwYXN0ZSgiUGxvdCBzYXZlZCBhczoiLG91dHB1dFBOR0ZpbGVOYW1lKSkNCmRldi5vZmYoKQ0KDQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhwYXN0ZTAoIkltYWdlcy8iLHJGaWxlTnVtLCJfcHFfcmV2aWV3L2NvdW50X3RvcGljX3llYXIucG5nIikpDQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhwYXN0ZTAoIkltYWdlcy8iLHJGaWxlTnVtLCJfcHFfcmV2aWV3L2NvdW50X3RvcGljX3llYXJfdG90LnBuZyIpKQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MocGFzdGUwKCJJbWFnZXMvIixyRmlsZU51bSwiX3BxX3Jldmlldy9yYXRpb190b3BpY195ZWFyLnBuZyIpKQ0KYGBgDQoNCiMgQ29oZXJlbmNlIHNjb3JlIGJ5IHRvcGljDQpgYGB7ciwgb3V0LndpZHRoPSI1MCUiLCBmaWcucG9zPSJoIn0NCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKHBhc3RlMCgiSW1hZ2VzLyIsckZpbGVNb2RlbE51bSwiX3BxX21vZGVsL2NvaGVyZW5jZV9zY29yZV90b3BpYy5wbmciKSkNCmBgYA0KDQojIFdvcmRjbG91ZHMNCmBgYHtyLCBvdXQud2lkdGg9IjUwJSIsIGZpZy5wb3M9ImgifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MocGFzdGUwKCJJbWFnZXMvIixyRmlsZU1vZGVsTnVtLCJfcHFfbW9kZWwvMV9sZGFfdG9waWNfd2MucG5nIikpDQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhwYXN0ZTAoIkltYWdlcy8iLHJGaWxlTW9kZWxOdW0sIl9wcV9tb2RlbC8yX2xkYV90b3BpY193Yy5wbmciKSkNCmBgYA0KIyBDb3VudCBvZiBBcnRpY2xlIGJ5IFRvcGljDQpgYGB7ciwgb3V0LndpZHRoPSI1MCUiLCBmaWcucG9zPSJoIn0NCiMgYmFyIGNoYXJ0DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhwYXN0ZTAoIkltYWdlcy8iLHJGaWxlTW9kZWxOdW0sIl9wcV9tb2RlbC90b3BpY19jb3VudF9iYXIucG5nIikpDQoNCiMgcGllIGNoYXJ0DQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhwYXN0ZTAoIkltYWdlcy8iLHJGaWxlTW9kZWxOdW0sIl9wcV9tb2RlbC90b3BpY19jb3VudF9waWUucG5nIikpDQpgYGANCg0KIyBDb3VudCBvZiBQcm9iYWJpbGl0aWVzIGJ5IFBlcmNlbnRhZ2UNCmBgYHtyLCBvdXQud2lkdGg9IjUwJSIsIGZpZy5wb3M9ImgifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MocGFzdGUwKCJJbWFnZXMvIixyRmlsZU1vZGVsTnVtLCJfcHFfbW9kZWwvcHJvYl9jb3VudC5wbmciKSkNCmBgYA0KIyBDb3VudCBvZiBUb3BpYyBQcm9iYWJpbGl0aWVzIGJ5IFBlcmNlbnRhZ2UNCmBgYHtyLCBvdXQud2lkdGg9IjUwJSIsIGZpZy5wb3M9ImgifQ0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MocGFzdGUwKCJJbWFnZXMvIixyRmlsZU1vZGVsTnVtLCJfcHFfbW9kZWwvcHJvYl90b3BpY19jb3VudC5wbmciKSkNCmBgYA0KIzUuIFZpc3VhbGlzaW5nIG9mIHRvcGljcyBpbiBhIGRlbmRyb2dyYW0gLSBub3QgZW5vdWdoIHRvcGljcyAoPDIpDQpgYGB7ciwgb3V0LndpZHRoPSI1MCUiLCBmaWcucG9zPSJoIn0NCiMga25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MocGFzdGUwKCJJbWFnZXMvIixyRmlsZU1vZGVsTnVtLCJfcHFfbW9kZWwvaGNsdXN0X2RlbmRyb2dyYW0ucG5nIikpDQoNCmBgYA0KDQojIFNUQVJUIFNVQlNFVFRJTkcgLSBTdWJzZXQgRnVuY3Rpb24NCiMjIyMgdG9waWNfc3Vic2V0X2NzdiA8LSBmdW5jdGlvbihvdXRwdXRGb2xkZXJOYW1lLCByRmlsZU51bSwgb3V0cHV0RmlsZU5hbWUsIGlucHV0Q29ycHVzLCBtaW5QZXJjLCBtYXhQZXJjLCB0aGVUb3BpYywgb3ZlcndyaXRlKQ0KDQoqIG91dHB1dEZvbGRlck5hbWUgPSBmb2xkZXIgdG8gc2F2ZSB0byAtLSBpLmUuLCAiRGF0YS8wMl9Xb3JraW5nLyIgKHRoaXMgaXMgc2V0IGF0IHRoZSB0b3Agb2YgdGhlIGNvZGUgd2hlcmUgdGhlIGxpYnJhcmllcyBhcmUgbG9hZGVkL2luc3RhbGxlZCkNCiogckZpbGVOdW0gPSBUaGUgbnVtYmVyIGluIHRoZSByIGZpbGUgLS0gaS5lLiwgIjA1IiAodGhpcyBpcyBzZXQgYXQgdGhlIHRvcCBvZiB0aGUgY29kZSB3aGVyZSB0aGUgbGlicmFyaWVzIGFyZSBsb2FkZWQvaW5zdGFsbGVkKQ0KKiBvdXRwdXRGaWxlTmFtZSA9IHRoZSBmaWxlIG5hbWUgdG8gc2F2ZSBhcyAtLSBpLmUuLCAicHFfdG9waWM1XzkwcGVyYyIgKHRoaXMgbmVlZHMgdG8gYmUgc2V0IGF0IGxlYXN0IDEgbGluZSBhYm92ZSB3aGVyZSB0aGUgZnVuY3Rpb24gaXMgcmFuKQ0KKiBpbnB1dENvcnB1cyA9IHRoZSBsYWJlbGVkIGNvcnB1cyAtLSBpLmUuLCBwcV9tZXRham9pbiAodGhpcyBpcyBjcmVhdGVkIHNob3J0bHkgYWZ0ZXIgd2hlcmUgbGlicmFyaWVzIGFyZSBsb2FkZWQvaW5zdGFsbGVkIGluIHJldmlldy5SbWQgYnkgam9pbmluZyBwcV9tZXRhY2xlYW4gd2l0aCBwcV9sYWJlbHMpDQoqIG1pblBlcmMgPSBtaW5pbXVtIHBlcmNlbnRhZ2UgLS0gaS5lLiwgMC45ICh0aGlzIG5lZWRzIHRvIGJlIHNldCBhdCBsZWFzdCAxIGxpbmUgYWJvdmUgd2hlcmUgdGhlIGZ1bmN0aW9uIGlzIHJhbikNCiogbWF4UGVyYyA9IG1heGltdW0gcGVyY2VudGFnZSAtLSBpLmUuLCAxICh0aGlzIG5lZWRzIHRvIGJlIHNldCBhdCBsZWFzdCAxIGxpbmUgYWJvdmUgd2hlcmUgdGhlIGZ1bmN0aW9uIGlzIHJhbikNCiogdGhlVG9waWMgPSB0aGUgdG9waWMgY2F0ZWdvcnkgLS0gaS5lLiwgIjEiICh0aGlzIG5lZWRzIHRvIGJlIHNldCBhdCBsZWFzdCAxIGxpbmUgYWJvdmUgd2hlcmUgdGhlIGZ1bmN0aW9uIGlzIHJhbikNCiogb3ZlcndyaXRlID0gd2hldGhlciB0byBvdmVyd3JpdGUgdGhlIGZpbGUgaWYgaXQgYWxyZWFkeSBleGlzdHMgLS0gaS5lLiwgRkFMU0UgKHRoaXMgbmVlZHMgdG8gYmUgc2V0IGF0IGxlYXN0IDEgbGluZSBhYm92ZSB3aGVyZSB0aGUgZnVuY3Rpb24gaXMgcmFuKQ0KDQojIFN1YnNldCB0b3BpYyAxDQpgYGB7cn0NCiMgc3Vic2V0IGNvcnB1cyB0byB1bmlxdWUgaWRlbnRpZmllciAmIGZ1bGwgdGV4dCBvZiBhcnRpY2xlDQojIGludmVzdGlnYXRlIHRvcGljIDENCg0Kb3V0cHV0RmlsZU5hbWVfMXQgPC0gInBxX3RvcGljMSINCm1pblBlcmMgPC0gMA0KbWF4UGVyYyA8LSAxDQp0aGVUb3BpYyA8LSAiMSINCg0KIyMgZS5nLCBtaW5QZXJjID0gMCAmIG1heFBlcmMgPSAwLjQgaXMgemVybyB0byAwLjM5OTk5OTk5OTkgLi4uDQpwcV90b3BpYzFfc3Vic2V0PC10b3BpY19zdWJzZXRfY3N2KG91dHB1dEZvbGRlciwgckZpbGVOdW0sIG91dHB1dEZpbGVOYW1lXzF0LCBwcV9tZXRham9pbiAsIG1pblBlcmMsIG1heFBlcmMsIHRoZVRvcGljLCBvdmVyd3JpdGUpDQoNCmhlYWQocHFfdG9waWMxX3N1YnNldCk7bnJvdyhwcV90b3BpYzFfc3Vic2V0KQ0KYGBgDQojIFN1YnNldCB0b3BpYyAyDQpgYGB7cn0NCiMgc3Vic2V0IGNvcnB1cyB0byB1bmlxdWUgaWRlbnRpZmllciAmIGZ1bGwgdGV4dCBvZiBhcnRpY2xlDQojIGludmVzdGlnYXRlIHRvcGljIDINCg0Kb3V0cHV0RmlsZU5hbWVfMXQgPC0gInBxX3RvcGljMiINCm1pblBlcmMgPC0gMA0KbWF4UGVyYyA8LSAxDQp0aGVUb3BpYyA8LSAiMiINCg0KIyMgZS5nLCBtaW5QZXJjID0gMCAmIG1heFBlcmMgPSAwLjQgaXMgemVybyB0byAwLjM5OTk5OTk5OTkgLi4uDQpwcV90b3BpYzFfc3Vic2V0PC10b3BpY19zdWJzZXRfY3N2KG91dHB1dEZvbGRlciwgckZpbGVOdW0sIG91dHB1dEZpbGVOYW1lXzF0LCBwcV9tZXRham9pbiAsIG1pblBlcmMsIG1heFBlcmMsIHRoZVRvcGljLCBvdmVyd3JpdGUpDQoNCmhlYWQocHFfdG9waWMxX3N1YnNldCk7bnJvdyhwcV90b3BpYzFfc3Vic2V0KQ0KYGBgDQoNCiMgU3Vic2V0IHRvcGljIDEsIDkwJQ0KYGBge3J9DQojIHN1YnNldCBjb3JwdXMgdG8gdW5pcXVlIGlkZW50aWZpZXIgJiBmdWxsIHRleHQgb2YgYXJ0aWNsZQ0KIyBpbnZlc3RpZ2F0ZSB0b3BpYyAxLCA5MCUNCm91dHB1dEZpbGVfMXQ5MHBlcmMgPC0gInBxX3RvcGljMV85MHBlcmMiDQptaW5QZXJjIDwtIDAuOQ0KbWF4UGVyYyA8LSAxDQp0aGVUb3BpYyA8LSAiMSINCg0KIyMgZS5nLCBtaW5QZXJjID0gMCAmIG1heFBlcmMgPSAwLjQgaXMgemVybyB0byAwLjM5OTk5OTk5OTkgLi4uDQpwcV90b3BpYzFfOTBwZXJjPC10b3BpY19zdWJzZXRfY3N2KG91dHB1dEZvbGRlciwgckZpbGVOdW0sIG91dHB1dEZpbGVfMXQ5MHBlcmMsIHBxX21ldGFqb2luICwgbWluUGVyYywgbWF4UGVyYywgdGhlVG9waWMsIG92ZXJ3cml0ZSkNCg0KaGVhZChwcV90b3BpYzFfOTBwZXJjKTtucm93KHBxX3RvcGljMV85MHBlcmMpDQpgYGANCg0KDQojIFN1YnNldCB0b3BpYyAyLCA5MCUNCmBgYHtyfQ0KIyBzdWJzZXQgY29ycHVzIHRvIHVuaXF1ZSBpZGVudGlmaWVyICYgZnVsbCB0ZXh0IG9mIGFydGljbGUNCiMgaW52ZXN0aWdhdGUgdG9waWMgMiwgOTAlDQpvdXRwdXRGaWxlXzV0OTBwZXJjIDwtICJwcV90b3BpYzJfOTBwZXJjIg0KbWluUGVyYyA8LSAwLjkNCm1heFBlcmMgPC0gMQ0KdGhlVG9waWMgPC0gIjIiDQoNCiMjIGUuZywgbWluUGVyYyA9IDAgJiBtYXhQZXJjID0gMC40IGlzIHplcm8gdG8gMC4zOTk5OTk5OTk5IC4uLg0KcHFfdG9waWM1XzkwcGVyYzwtdG9waWNfc3Vic2V0X2NzdihvdXRwdXRGb2xkZXIsIHJGaWxlTnVtLCBvdXRwdXRGaWxlXzV0OTBwZXJjLCBwcV9tZXRham9pbiAsIG1pblBlcmMsIG1heFBlcmMsIHRoZVRvcGljLCBvdmVyd3JpdGUpDQoNCmhlYWQocHFfdG9waWM1XzkwcGVyYyk7bnJvdyhwcV90b3BpYzVfOTBwZXJjKQ0KYGBgDQoNCg0KIyBTdWJzZXQgNTAtNjAlLWVycw0KYGBge3J9DQojIHN1YnNldCBjb3JwdXMgdG8gdW5pcXVlIGlkZW50aWZpZXIgJiBmdWxsIHRleHQgb2YgYXJ0aWNsZQ0KIyBpbnZlc3RpZ2F0ZSBhbGwgdG9waWNzLCA1MC02MCUNCm91dHB1dEZpbGVfNTZwZXJjIDwtICJwcV81NnBlcmMiDQptaW5QZXJjIDwtIDAuNQ0KbWF4UGVyYyA8LSAwLjYNCnRoZVRvcGljIDwtICJhbGwiDQoNCiMjIGUuZywgbWluUGVyYyA9IDAgJiBtYXhQZXJjID0gMC40IGlzIHplcm8gdG8gMC4zOTk5OTk5OTk5IC4uLg0KcHFfNTZwZXJjPC10b3BpY19zdWJzZXRfY3N2KG91dHB1dEZvbGRlciwgckZpbGVOdW0sIG91dHB1dEZpbGVfNTZwZXJjLCBwcV9tZXRham9pbiAsIG1pblBlcmMsIG1heFBlcmMsIHRoZVRvcGljLCBvdmVyd3JpdGUpDQoNCmhlYWQocHFfNTZwZXJjKTtucm93KHBxXzU2cGVyYykNCmBgYA0KDQojIFN1YnNldCAwLTYwJS1lcnMNCmBgYHtyfQ0KIyBzdWJzZXQgY29ycHVzIHRvIHVuaXF1ZSBpZGVudGlmaWVyICYgZnVsbCB0ZXh0IG9mIGFydGljbGUNCiMgaW52ZXN0aWdhdGUgYWxsIHRvcGljcywgMC02MCUNCiMgaWYgd2l0aGluIHRoZSBmdW5jdGlvbiB5b3Ugd3JpdGUgb3ZlcndyaXRlPVRSVUUsIHRoZW4gdGhlIG91dHB1dCBDU1YgZmlsZSB3aWxsIGJlIHJlLXdyaXR0ZW4uIA0KIyBvdmVyd3JpdGUsIG91dHB1dEZvbGRlciwgckZpbGVOYW1lIGFyZSBzZXQgaW4gdGhlIGZpcnN0IGNodW5rIG9mIGNvZGUNCg0KDQpvdXRwdXRGaWxlTmFtZV8wNnBlcmMgPC0gInBxXzA2cGVyYyINCm1pblBlcmMgPC0gMA0KbWF4UGVyYyA8LSAwLjYNCnRoZVRvcGljIDwtICJhbGwiDQoNCiMjIGUuZywgbWluUGVyYyA9IDAgJiBtYXhQZXJjID0gMC42IGlzIHplcm8gdG8gMC41OTk5OTk5OSAuLi4NCnBxX3BlcmMwNl9zdWJzZXQ8LXRvcGljX3N1YnNldF9jc3Yob3V0cHV0Rm9sZGVyLCByRmlsZU51bSwgb3V0cHV0RmlsZU5hbWVfMDZwZXJjLCBwcV9tZXRham9pbiAsIG1pblBlcmMsIG1heFBlcmMsIHRoZVRvcGljLCBvdmVyd3JpdGUpDQoNCmhlYWQocHFfcGVyYzA2X3N1YnNldCk7bnJvdyhwcV90b3BpYzFfc3Vic2V0KQ0KdW5pcXVlKHBxX3BlcmMwNl9zdWJzZXQkdG9waWMpDQptaW4ocHFfcGVyYzA2X3N1YnNldCR2YWwpDQptYXgocHFfcGVyYzA2X3N1YnNldCR2YWwpDQpgYGANCg0KIyBFTkQgU1VCU0VUVElORw0KDQojIGV4cGxvcmUgdGVybSBmcmVxdWVuY2llcyBieSB5ZWFyIC0gaG93IGRvIHdvcmRzIGluIG91ciBjb3JwdXMgY2hhbmdlIG92ZXIgdGltZT8NClJlZmVyZW5jZWQgd2Fsay10aHJvdWdoIGZyb206IGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy90aWR5dGV4dC92aWduZXR0ZXMvdGlkeWluZ19jYXN0aW5nLmh0bWwNCkFyZSBhcnRpY2xlcyBub3JtYWxpemVkIGJhc2VkIG9uIHRoZSBudW1iZXIgb2YgYXJ0aWNsZXMgcGVyIHllYXINCmBgYHtyfQ0KIyBzdWJzZXQgY29ycHVzIHRvIHVuaXF1ZSBpZGVudGlmaWVyICYgeWVhcg0KcHFfdGltZSA8LSBwcV9tZXRhY2xlYW4gJT4lIA0KICBzZWxlY3QoYFByb1F1ZXN0IGRvY3VtZW50IElEYCwgYFB1YmxpY2F0aW9uIHllYXJgKQ0KDQojIHRva2VucyBnZW5lcmF0ZWQgZnJvbSAwNl9wcV9tb2RlbC5SbWQNCm91dHB1dFRva2VuRmlsZSA9IHBhc3RlMChyRmlsZU1vZGVsTnVtLCJfdG9rZW5zLlJEYXRhIikNCnRva2Vuc0ZpbGVOYW1lID0gZmlsZS5wYXRoKG91dHB1dEZvbGRlcixvdXRwdXRUb2tlbkZpbGUpDQoNCmxvYWQoZmlsZT10b2tlbnNGaWxlTmFtZSkNCg0KdG9rZW5zIDwtIHRva2VucyAlPiUgDQogIGZ1bGxfam9pbihwcV90aW1lLCBieSA9IGMoIlByb1F1ZXN0IGRvY3VtZW50IElEIiA9ICJQcm9RdWVzdCBkb2N1bWVudCBJRCIpKSAlPiUNCiAgcmVuYW1lKFllYXIgPSBgUHVibGljYXRpb24geWVhcmApICU+JQ0KICByZW5hbWUocHFfaWQgPSBgUHJvUXVlc3QgZG9jdW1lbnQgSURgKSAlPiUNCiAgbXV0YXRlX2F0KHZhcnMoWWVhciksIGZ1bnMoYXMuaW50ZWdlcikpDQpybShwcV90aW1lKQ0KDQpvdXRwdXRGcmVxc0ZpbGUgPSBwYXN0ZTAockZpbGVOdW0sIl90b2tlbnNfZnJlcSIpDQp0b2tlbnNfZnJlcTwtY3JlYXRlX2lmbm90X3Rva2Vuc19mcmVxKG91dHB1dEZvbGRlciwgb3V0cHV0RnJlcXNGaWxlLCB0b2tlbnMsIG92ZXJ3cml0ZSkNCg0Kb3V0cHV0RnJlcU1vZGVsRmlsZSA9IHBhc3RlMChyRmlsZU51bSwiX2ZyZXFfbW9kZWxzIikNCmZyZXFfbW9kZWxzIDwtIGNyZWF0ZV9pZm5vdF9mcmVxbW9kZWxzKG91dHB1dEZvbGRlciwgb3V0cHV0RnJlcU1vZGVsRmlsZSwgdG9rZW5zX2ZyZXEsIG92ZXJ3cml0ZSkNCmBgYA0KDQojIG1vZGVsIHJlc3VsdHMNCmBgYHtyfQ0KZnJlcV9tb2RlbHMgJT4lDQogIGZpbHRlcih0ZXJtID09ICJZZWFyIikgJT4lDQogIGFycmFuZ2UoZGVzYyhhYnMoZXN0aW1hdGUpKSkNCmBgYA0KDQojIE1vZGVscyBkaXNwbGF5ZWQgYXMgYSB2b2xjYW5vIHBsb3QsIHdoaWNoIGNvbXBhcmVzIHRoZSBlZmZlY3Qgc2l6ZSB3aXRoIHRoZSBzaWduaWZpY2FuY2UNCmBgYHtyfQ0KIyBzYXZlIHBsb3QgaW4gcG5nIGZvcm1hdA0Kb3V0cHV0UE5HRmlsZU5hbWUgPC0gZmlsZS5wYXRoKG91dHB1dFBuZ0ZvbGRlcixwYXN0ZTAoIndvcmRfY2hhbmdlX292ZXJfdGltZS5wbmciKSkNCnBuZyhvdXRwdXRQTkdGaWxlTmFtZSxoZWlnaHQ9NSx3aWR0aD0xNSwgdW5pdHM9J2luJywgcmVzPTMwMCkNCg0KZnJlcV9tb2RlbHMgJT4lDQogIG11dGF0ZShhZGp1c3RlZC5wLnZhbHVlID0gcC5hZGp1c3QocC52YWx1ZSkpICU+JQ0KICBnZ3Bsb3QoYWVzKGVzdGltYXRlLCBhZGp1c3RlZC5wLnZhbHVlKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBzY2FsZV95X2xvZzEwKCkgKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsID0gd29yZCksIHZqdXN0ID0gMSwgaGp1c3QgPSAxLA0KICAgICAgICAgICAgY2hlY2tfb3ZlcmxhcCA9IFRSVUUpICsNCiAgeGxhYigiRXN0aW1hdGVkIGNoYW5nZSBvdmVyIHRpbWUiKSArDQogIHlsYWIoIkFkanVzdGVkIHAtdmFsdWUiKQ0KDQoNCnByaW50KHBhc3RlKCJQbG90IHNhdmVkIGFzOiIsb3V0cHV0UE5HRmlsZU5hbWUpKQ0KZGV2Lm9mZigpDQoNCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKHBhc3RlMCgiSW1hZ2VzLyIsckZpbGVOdW0sIl9wcV9yZXZpZXcvd29yZF9jaGFuZ2Vfb3Zlcl90aW1lLnBuZyIpKQ0KYGBgDQoNCiMgVG9wIDYgdGVybXMgdGhhdCBoYXZlIGNoYW5nZWQgaW4gZnJlcXVlbmN5IG92ZXIgdGltZQ0KYGBge3J9DQojIHNhdmUgcGxvdCBpbiBwbmcgZm9ybWF0DQpvdXRwdXRQTkdGaWxlTmFtZSA8LSBmaWxlLnBhdGgob3V0cHV0UG5nRm9sZGVyLHBhc3RlMCgidG9wX3dvcmRfY2hhbmdlX292ZXJfdGltZS5wbmciKSkNCnBuZyhvdXRwdXRQTkdGaWxlTmFtZSxoZWlnaHQ9NSx3aWR0aD0xNSwgdW5pdHM9J2luJywgcmVzPTMwMCkNCg0KZnJlcV9tb2RlbHMgJT4lDQogIHRvcF9uKDYsIGFicyhlc3RpbWF0ZSkpICU+JQ0KICBpbm5lcl9qb2luKHRva2Vuc19mcmVxKSAlPiUNCiAgZ2dwbG90KGFlcyhZZWFyLCBwZXJjZW50KSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3Ntb290aCgpICsNCiAgZmFjZXRfd3JhcCh+IHdvcmQpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHBlcmNlbnRfZm9ybWF0KCkpICsNCiAgeWxhYigiRnJlcXVlbmN5IG9mIHdvcmQgaW4gc3BlZWNoIikNCg0KcHJpbnQocGFzdGUoIlBsb3Qgc2F2ZWQgYXM6IixvdXRwdXRQTkdGaWxlTmFtZSkpDQpkZXYub2ZmKCkNCg0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MocGFzdGUwKCJJbWFnZXMvIixyRmlsZU51bSwiX3BxX3Jldmlldy90b3Bfd29yZF9jaGFuZ2Vfb3Zlcl90aW1lLnBuZyIpKQ0KYGBgDQo=